package config

import (
	"bufio"
	"io"
	"os"
	"reflect"
	"strconv"
	"strings"
)

type ServerProperties struct {
	Bind              string `cfg:"bind"`
	Port              int    `cfg:"port"`
	AppendOnly        bool   `cfg:"appendonly"`
	AppendFilename    string `cfg:"appendfilename"`
	MaxClients        int    `cfg:"maxclients"`
	RequirePass       string `cfg:"requirepass"`
	Databases         int    `cfg:"databases"`
	RDBFilename       string `cfg:"dbfilename"`
	MasterAuth        string `cfg:"masterauth"`
	SlaveAnnouncePort int    `cfg:"slave-announce-port"`
	SlaveAnnounceIP   string `cfg:"slave-announce-ip"`
	ReplTimeout       int    `cfg:"repl-timeout"`

	Peers []string `cfg:"peers"`
	Self  string   `cfg:"self"`
}

var PropPtr *ServerProperties

func init() {
	PropPtr = &ServerProperties{
		Bind:       "127.0.0.1",
		Port:       6379,
		AppendOnly: false,
	}
}

func DefaultConfig() {
	PropPtr = &ServerProperties{
		Bind:           "0.0.0.0",
		Port:           6399,
		AppendOnly:     false,
		AppendFilename: "",
		MaxClients:     1000,
	}
}

// ReadConfig 读取redis.conf配置
func ReadConfig(configFileName string) {
	filePtr, err := os.Open(configFileName)
	if err != nil {
		panic("config read fail")
	}
	defer filePtr.Close()
	// 解析redis.conf文件
	PropPtr = parseConfig(filePtr)
}

// parseConfig 配置文件解析
func parseConfig(reader io.Reader) *ServerProperties {
	scannerPtr := bufio.NewScanner(reader)
	configMap := make(map[string]string)
	for scannerPtr.Scan() {
		line := scannerPtr.Text()
		// 注释的行不解析
		if len(line) > 0 && line[0] == '#' {
			continue
		}
		// = 分隔
		splitIndex := strings.IndexAny(line, "=")
		if splitIndex > 0 && splitIndex < len(line) {
			key := line[:splitIndex]
			value := strings.Trim(line[splitIndex+1:], " ")
			configMap[key] = value
		}
	}

	// 通过反射赋值
	configPtr := &ServerProperties{}
	reflectType := reflect.TypeOf(configPtr)
	reflectValue := reflect.ValueOf(configPtr)
	fieldNum := reflectType.Elem().NumField()
	for i := 0; i < fieldNum; i++ {
		field := reflectType.Elem().Field(i)
		fieldValue := reflectValue.Elem().Field(i)
		key, ok := field.Tag.Lookup("cfg")
		if !ok {
			key = field.Name
		}
		configValue, ok := configMap[key]
		if ok {
			switch field.Type.Kind() {
			case reflect.String:
				fieldValue.SetString(configValue)
			case reflect.Bool:
				boolValue := "yes" == configValue
				fieldValue.SetBool(boolValue)
			case reflect.Int:
				intValue, err := strconv.ParseInt(configValue, 10, 64)
				if err == nil {
					fieldValue.SetInt(intValue)
				}
			case reflect.Slice:
				if field.Type.Elem().Kind() == reflect.String {
					valueSlc := strings.Split(configValue, ",")
					fieldValue.Set(reflect.ValueOf(valueSlc))
				}
			}
		}
	}
	configPtr.Self = configPtr.Bind + ":" + strconv.Itoa(configPtr.Port)
	return configPtr
}
